package db

import (
	"center/db/models"
	"common/db/fields"
	"common/rs"
	"common/uuid"
	"context"
	"database/sql"
	"errors"
	"fmt"
	time2 "github.com/wuzfei/go-helper/time"
	"gorm.io/gorm"
	"time"
)

type CreateObjectParam struct {
	StreamId         uuid.UUID
	BucketId         uuid.UUID
	EncryptObjectKey []byte
	EncryptedKey     []byte
	ObjectKey        string
}

// CreateObj 上传时创建object， 并入数据库
func (d *CenterDB) CreateObj(ctx context.Context, params *CreateObjectParam) (object *models.Object, err error) {
	object = &models.Object{
		StreamId:         params.StreamId,
		BucketId:         params.BucketId,
		EncryptObjectKey: string(params.EncryptObjectKey),
		EncryptedKey:     params.EncryptedKey,
		Status:           models.ObjectStatusPending,
	}
	err = d.db.WithContext(ctx).Create(&object).Error
	return
}

type BeginObjectParam struct {
	StreamId         uuid.UUID
	BucketId         uuid.UUID
	EncryptObjectKey []byte
	EncryptedKey     []byte
	ObjectKey        string
	ExpireTime       time.Time
	Attribute        fields.Attribute
}

func (d *CenterDB) InsertOrCreateInlineObject(ctx context.Context, param *BeginObjectParam,
	inlineExist bool, data []byte) (err error) {
	et := sql.NullTime{}
	if !param.ExpireTime.IsZero() {
		et.Time = param.ExpireTime
		et.Valid = true
	}

	dataObj := models.ObjectInline{
		StreamId: param.StreamId,
		Data:     data,
	}
	fmt.Println(param.StreamId.String())
	var exist = models.ObjectInline{}
	err = d.db.WithContext(ctx).Where("stream_id=?", param.StreamId).First(&exist).Error
	if err != nil {
		fmt.Println("inline Create")
		err = d.db.WithContext(ctx).Create(&dataObj).Error
	} else {
		fmt.Println("inline Updates")
		err = d.db.WithContext(ctx).Updates(&dataObj).Error
	}
	if err != nil {
		fmt.Println("end insert inline obj", err)
		return
	}

	var object *models.Object
	object = &models.Object{
		StreamId:         param.StreamId,
		BucketId:         param.BucketId,
		EncryptObjectKey: string(param.EncryptObjectKey),
		EncryptedKey:     param.EncryptedKey,
		Status:           models.ObjectStatusCommitted,
		ExpireTime:       et,
		Inline:           true,
	}
	if !inlineExist {
		fmt.Println("Object Create")
		err = d.db.WithContext(ctx).Create(&object).Error
	}

	if err != nil {
		fmt.Println("end insert obj", err)
		return
	}
	return
}

// BeginObject 上传时创建object， 并入数据库
func (d *CenterDB) BeginObject(ctx context.Context, param *BeginObjectParam) (object *models.Object, err error) {
	et := sql.NullTime{}
	if !param.ExpireTime.IsZero() {
		et.Time = param.ExpireTime
		et.Valid = true
	}

	object = &models.Object{
		StreamId:         param.StreamId,
		BucketId:         param.BucketId,
		EncryptObjectKey: string(param.EncryptObjectKey),
		EncryptedKey:     param.EncryptedKey,
		Status:           models.ObjectStatusPending,
		ExpireTime:       et,
		Inline:           false,
		Attribute:        param.Attribute,
	}
	err = d.db.WithContext(ctx).Create(&object).Error
	return
}

type ObjectOne struct {
	EncryptObjectKey []byte    `json:"encrypt_object_key"`
	BucketId         uuid.UUID `json:"bucket_id"`
}

type CommitObjectParam struct {
	StreamId           uuid.UUID
	EncryptedObjectKey []byte
	PlainSize          int64
	SegmentCount       int
	SegmentSize        int
	Rs                 rs.Rs
}

func (d *CenterDB) CommitObject(ctx context.Context, param *CommitObjectParam) error {
	res := d.db.WithContext(ctx).Model(&models.Object{}).Where("stream_id = ?", param.StreamId).Updates(&models.Object{
		PlainSize:    param.PlainSize,
		SegmentCount: param.SegmentCount,
		SegmentSize:  param.SegmentSize,
		Redundancy:   param.Rs,
		Status:       models.ObjectStatusCommitted,
	})
	if res.Error != nil {
		return res.Error
	}
	if res.RowsAffected == 0 {
		return errors.New("更新失败，更新数量为0")
	}
	return nil
}

func (d *CenterDB) GetObject(ctx context.Context, obj ObjectOne) (object *models.Object, err error) {
	err = d.db.WithContext(ctx).Where(obj).Take(&object).Error
	return
}

// GetObjectByStreamId 根据streamId查找对象
func (d *CenterDB) GetObjectByStreamId(ctx context.Context, streamId []byte) (object *models.Object, err error) {
	err = d.db.WithContext(ctx).Where("stream_id = ?", streamId).Take(&object).Error
	return
}
func (d *CenterDB) GetInlineByStreamId(ctx context.Context, streamId []byte) ([]byte, error) {

	dataObj := models.ObjectInline{}
	err := d.db.WithContext(ctx).Model(&models.ObjectInline{}).Where("stream_id = ?", streamId).First(&dataObj).Error
	if err != nil {
		return nil, err
	}
	return dataObj.Data, nil
}
func (d *CenterDB) GetObjectWithSegments(ctx context.Context, obj ObjectOne) (object *models.Object, err error) {
	err = d.db.WithContext(ctx).Where(obj).Preload("Segments").Take(&object).Error
	return
}

// DeleteObject 删除对象
func (d *CenterDB) DeleteObject(ctx context.Context, obj ObjectOne) error {
	object := models.Object{}
	err := d.db.WithContext(ctx).Where(obj).Take(&object).Error
	if err != nil {
		return err
	}
	return d.deleteObjectByStreamId(ctx, object.StreamId)
}

// DeleteObjectByStreamId 删除对象指定对象，这里只对数据库删除
func (d *CenterDB) DeleteObjectByStreamId(ctx context.Context, streamId uuid.UUID) error {
	return d.deleteObjectByStreamId(ctx, streamId)
}

func (d *CenterDB) deleteObjectByStreamId(ctx context.Context, streamId uuid.UUID) error {
	if streamId.IsZero() {
		return errors.New("id error")
	}
	return d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		//删除object
		err := tx.Where("stream_id = ?", streamId).Delete(&models.Object{}).Error
		if err != nil {
			return err
		}
		//删除segments
		err = tx.Model(models.Segment{}).Where("stream_id = ?", streamId).
			Delete(&models.Segment{}).Error
		if err != nil {
			return err
		}
		//删除修复队列
		return tx.Model(models.Repair{}).Where("stream_id = ?", streamId).
			Delete(&models.Segment{}).Error
	})
}

// DeleteObjectExpire 删除过期对象
func (d *CenterDB) DeleteObjectExpire(ctx context.Context) (streamIds []uuid.UUID, err error) {
	return nil, nil
	//return d.db.WithContext(ctx).Where("expire_time != 0 and expire_time <= ?", exp).
	//Delete(&models.Object{}).Error
}

// GetObjectsByStreamIds 根据stream_id获取文件
func (d *CenterDB) GetObjectsByStreamIds(ctx context.Context, streamIds []uuid.UUID) (objs []*models.Object, err error) {
	objs = make([]*models.Object, 0, len(streamIds))
	if len(streamIds) == 0 {
		return
	}
	err = d.db.WithContext(ctx).
		Where("stream_id in ?", streamIds).
		Find(&objs).Error
	return
}

// GetObjectsByStreamIdsEWithoutExpire 根据stream_id获取未过期文件
func (d *CenterDB) GetObjectsByStreamIdsEWithoutExpire(ctx context.Context, streamIds []uuid.UUID) (objs []*models.Object, err error) {
	objs = make([]*models.Object, 0, len(streamIds))
	if len(streamIds) == 0 {
		return
	}
	err = d.db.WithContext(ctx).
		Where("stream_id in ? and (expire_time != 0 and expire_time > ?)", streamIds, time.Now().Format(time2.DefaultDateTimeLayout)).
		Find(&objs).Error
	return
}

type ListObjects struct {
	BucketId         uuid.UUID
	Recursive        bool
	Limit            int
	Prefix           []byte
	Cursor           []byte
	EncryptObjectKey [][]byte
	Status           uint8
}

func (d *CenterDB) ListObjects(ctx context.Context, opts ListObjects) (data []*models.Object, err error) {
	db := d.db.WithContext(ctx).Where("bucket_id = ?", opts.BucketId)
	if len(opts.Cursor) > 0 {
		db = db.Where("encrypt_object_key > ", opts.Cursor)
	}
	if opts.Status > 0 {
		db = db.Where("status = ?", opts.Status)
	}
	if len(opts.Prefix) > 0 {
		db = db.Where("encrypt_object_key like ?", string(opts.Prefix)+"%")
	}
	if len(opts.EncryptObjectKey) > 0 {
		db = db.Where("encrypt_object_key in ?", opts.EncryptObjectKey)
	}
	limit := 1000
	if opts.Limit > 0 && opts.Limit <= limit {
		limit = opts.Limit
	}
	db = db.Limit(limit)
	err = db.Find(&data).Order("encrypt_object_key desc").Error
	return
}

func (d *CenterDB) DetailObject(ctx context.Context, bucketId uuid.UUID, encryptObjectKey string) (data *models.Object, err error) {
	err = d.db.WithContext(ctx).Where("bucket_id = ? and encrypt_object_key = ? ", bucketId, encryptObjectKey).First(&data).Error
	return
}
